USE OF WINDOWS 3.1 API CALLS FROM A FORTRAN DLL TO A VISUAL BASIC PROGRAM ========================================================================= Tony Lewis, September 1994, Rev. 1 CIS 73357,1730 ABSTRACT -------- These files demonstrate how a Dynamic Link Library (DLL) written in Fortran can use Windows 3.1 Applications Programming Interface (API) calls to send status messages to a Visual Basic program while the DLL is executing. The demonstration program uses API calls as follows: > Create a Message Box to communicate with the program user > Send a status message string to a VB text box (current status message) > Send a status message string to a VB list box (status message log) > use of the UpdateWindow API call to refresh the VB form display This method of using API calls to communicate to the main Visual Basic program may be applicable for DLL's written in languages besides Fortran. Note that this method of using API calls with a Fortran DLL may not be optimal, but it does appear to work without errors. KEYWORDS -------- Visual Basic, Fortran, DLL, API, Windows, SendMessage, Message Box, UpdateWindow LANGUAGES --------- Microsoft Visual Basic 3.0, Fortran 5.1 BACKGROUND ---------- Although the Advanced Topics manual for MS Fortran 5.1 states that Fortran cannot be used to call Windows API functions, other references provide examples of how it can be done [see the REFERENCES section]. Windows API calls can be utilized to build a complete Windows application (not a QuickWin application), complete with menus, dialog boxes, etc. This functionality also extends to Fortran DLLs as well. Use of Windows API calls in a Fortran program to create a stand alone Windows application can be a challenging task, particularly if one knows little or nothing about using the API calls. Many programmers have found it advantageous to use Visual Basic for the user interface, and convert the Fortran "number crunching" program to a DLL, which is called by VB. For short functions and subroutines, execution time is not significant, and the end user probably never knows (or cares) that a DLL was used. However, if the Fortran DLL is large, and performs numerous tasks (disk I/O, numeric intensive calculations, etc.), then the user may wind up staring at a "Calculating; please wait..." message while the DLL goes about its many tasks. These files demonstrate how a Fortran DLL can utilize Windows API calls to pass status messages to a Visual Basic text box and list box while the DLL is executing. The text box is used to show the current status message, while the list box is used to store all status messages received, for later review. In addition, examples of generating Message Boxes and updating the VB window display are included. By using these API calls, one may create VB/Fortran DLL programs that allow the DLL to pass status, warning, or error messages from the DLL to the VB form. [Note that this method is a 'one way street' in that VB cannot pass messages back to the DLL during execution; it is assumed that the interface between VB and the subs/functions in the DLL will pass all needed information when the DLL is called.] WINDOWS API CALLS ----------------- Windows 3.1 can be considered to be a large collection of dynamic link libraries, which are used to perform various functions, such as creating windows, copying information to the clipboard, and managing printers. A lot of programs written for Windows are done in C, which is not surprising since it provides a lot of flexibility. But it does require the programmer to become intimate with the internals of Windows itself. Visual Basic provides most of the functionality of a 'real' Windows program, while shielding the programmer from the API calls. Microsoft Fortran 5.1 does not provide any support for using API calls, so it is up to the programmer to add the various interfaces to the Fortran program or DLL. Consult the references given at the end, the Windows Software Development Kit (SDK), or other third party books to learn more about API calls. The Professional version of VB 3.0 provides several help files on use of API calls. WINDOWS API CALLS - MessageBox function --------------------------------------- Fortran 5.1 provides the MESSAGBOXQQ subroutine to generate a message box from a program. This could, in theory, be used to transmit status messages from a running DLL to a VB program. However, if the possibility of several messages exist, the user may tire quickly from clicking the "OK" button over and over. The MessageBox API call is provided in the DLL to provide an example of its usage, and to provide notification of the start and end of the DLL execution. The interface for MessageBox is given below (in $FREEFORM format): interface to integer*2 FUNCTION MessageBox[pascal]- (hWndMB,boxtext,caption,boxtype) integer*2 hWndMB[value] integer*2 boxtype[value] character*1 boxtext[reference] character*1 caption[reference] end The MessageBox function is denoted as PASCAL, as are the SendMessage function and UpdateWindow subroutine. The hWndMB value is the Windows handle of the message box, and is set to zero for this function. The boxtype value determines the various features of the message box (buttons and icons); see the Advanced Topics manual for details. The actual message is passed via boxtext, and the caption for the message box is passed via caption. Note that the actual length of the text strings are set within the program, and are passed by reference to the function when it is called. The strings must also be "C" strings with a null character as the end character. When used in a DLL, the MessageBox function provides the same function as MessageBoxQQ in a program, or MsgBox in Visual Basic. WINDOWS API CALLS - UpdateWindow subroutine ------------------------------------------- Most standalone Windows applications due their own housekeeping with regards to the display of the application's windows, and components within the windows. In this application, a Fortran DLL is modifying a text box and a list box of a Visual Basic form; VB is not 'aware' that the DLL is doing this, so the display of both boxes may not reflect what the DLL has inserted. To get around this, a call to the UpdateWindow subroutine is used after the DLL has sent the string to the text and list boxes. This subroutine tells the VB application that it needs to repaint the VB form, which also refreshes the display for the list and text boxes. The UpdateWindow subroutine only needs the handle of the VB form (Form1.hWnd) to know which window needs refreshing. The interface to the UpdateWindow subroutine is given below: interface to SUBROUTINE UpdateWindow[pascal](hWndVBF) integer*2 hWndVBF[value] end WINDOWS API CALLS - SendMessage function ---------------------------------------- The SendMessage function is a versatile way for the various components of Windows and applications to communicate information and commands. As the name implies, it is used to send messages to other applications. In the example DLL, we use SendMessage to send a text string to a VB text box, and send the same text string to a VB list box. As noted earlier, the text box can be used to provide status messages to the user while the DLL is running. The list box can be used to keep a log of all messages received from the DLL, for review after the DLL is finished. Once the text strings are sent to the text and list box, VB can manipulate the strings as if VB itself had placed the text in the text and list boxes. The format for the SendMessage function is given below (in $FREEFORM format): interface to integer*4 FUNCTION SendMessage[pascal]- (hWnd, WinMsg, wparam, lparam) character*1 lparam[reference] integer*2 hWnd[value] integer*2 WinMsg[value] integer*2 wparam[value] end The hWnd value is the handle of the item to receive the message. In this example, we need the handle of the text box and the list box. For Visual Basic 3.0, this is easily accomplished by the following code: LBhandle% = List1.hand TBhandle% = Text1.hand The handle is passed to the DLL in its argument list when it is called; in the example code the handles for the text and list boxes are passed (along with the VB form handle). The WinMsg value is the value of the message passed by SendMessage. The constants for various messages can be found in the files included with the Professional version of VB, or other sources. The wparam value is a 16 bit variable that has specific uses, depending upon which message that SendMessage is transmitting; in this case, it is zero for both messages. The lparam value is a 32 bit (integer*4) value that is used in both messages as a pointer to the string to sent to VB. It's use also varies depending upon the message used with SendMessage. The strings to be sent to VB have to be null terminated "C" strings, as with MessageBox. The programmer should insure that the string length of the text does not exceed the size of the text box and list box in VB (for readability). To send a text string to a VB text box, the WM_SETTEXT message (hex 0401)is sent via SendMessage, after defining the string. The call to SendMessage(WM_SETTEXT) can be done where ever the programmer desires to send a status, warning, or error message during execution of the DLL. Note that the time that a message is displayed in the VB text box depends upon when the DLL issues the next SendMessage call; therefore, it may not be physically possible for the user to read all messages before they are replaced by the next message. A list box can be used to log all the messages. Visual Basic list boxes automatically add scroll bars when the number of items received exceed the number of visible lines. This provides a handy way for the program or user to review all status messages when the DLL is complete. To send a text string to a VB list box, the LB_ADDSTRING message (hex C) is sent via SendMessage. In the example code, the same string, text1, is sent to both the text and list box. An extra feature of using LB_ADDSTRING for the list box is that the return value (SendListVal) is the item number of the text added to the list box in VB. EXAMPLE PROGRAM - Overview -------------------------- The example programs consist of a VB program and a Fortran DLL. The VB program calls the DLL, which generates an introductory message box, sends nine status messages to the text box and list box, and generates an 'all done' message box before returning to VB. The status messages are received approximately every 4 seconds, to simulate the effect of a running DLL. EXAMPLE PROGRAM - testmsg (Visual Basic 3.0) ------------------------------------------------ The VB program testmsg consists of one form, with one text box (text1), one list box (list1) and three command buttons (CallDLLCmdButton, ClearCmdButton, and OKCmdButton). In addition, a label (label3) is provided at the top of the form to indicate the program status (in VB vs. in DLL). The Call DLL button will update the program status label, and call the DLL. As the DLL runs, the text in the text box and list box will be updated by the DLL. When the DLL is complete, the last DLL status message is shown in the text box, and all nine messages are in the list box, which now has scroll bars. The Clear Box button will clear both boxes of items, if desired. The OK button terminates the application. Note that the VB program does not use any API calls itself. The interface to the Fortran DLL is given in the testmsg.bas file: Declare Sub TESTMSG Lib "testmsg.dll"(ListBoxhWnd As Integer, TextBoxhWnd As Integer, VBFormhWnd as Integer) Here, the handles to the text box, the list box and the VB form (window) are passed to the DLL as integers (integer*2). The example DLL must be in the PATH for the VB application to run; SETUP will place the DLL in the \WINDOWS\SYSTEM directory. EXAMPLE PROGRAM - testmsg.dll (Fortran 5.1) ------------------------------------------- The testmsg.dll consists of one subroutine, also called testmsg. The interface to SendMessage, MessageBox, and UpdateWindow are given up front. All variables are explicitly declared, as this practice contributes to better memory management for Windows (i.e. - reduces chances for General Protection Faults). The SendMessage message constants are defined, as well as wparam. The first message box informs the user that the DLL is about to begin to send status messages. A DO loop sends nine status messages to the VB text and list boxes, via the two SendMessage functions. The UpdateWindow subroutine is called to refresh the VB form display. A simple Do While loop generates a 4 second delay between sending status messages to simulate the effect of a running DLL. A second 'all done' message box is generated when the nine status messages have been sent, and the DLL returns to Visual Basic. COMPILING THE FORTRAN DLL ------------------------- The testmsg.for file can be compiled from the command line as follows (this is given in the file compdll.bat): FL /c /Aw /Gw a:\testmsg.for The /c switch compiles without linking (that's next). The /Aw and /Gw switches are needed to generate windows compatible code for a DLL. Note that the /MW switch is not used for a DLL. THE DLL DEFINITION FILE - testmsg.def ------------------------------------- A definition file for the DLL is needed to tell the linker how to assemble the final product. The definition file for this example code is given below: ; TESTMSG.DEF for compiling DLL LIBRARY TESTMSG APPLOADER '__MSLANGLOAD' EXETYPE WINDOWS 3.1 PROTMODE CODE PRELOAD MOVEABLE DISCARDABLE DATA PRELOAD MOVEABLE SINGLE HEAPSIZE 1024 EXPORTS TESTMSG WEP Consult the references for defining the various inputs to the definition file for a DLL. Note that the LIBRARY name must match the filename of the DLL, and the EXPORTS must have the name of the subroutine (also called testmsg) and WEP for the DLL to work. LINKING THE OBJECT FILE (linkdll.bat) ------------------------------------- The DLL may be linked from the command line with the following: LINK testmsg.obj, testmsg.dll, nul, /NOD /NOE ldllfew.lib noqwin.lib, testmsg.def Note that the command line for the linking references the object file created by the previous compilation of the source code, and names the output with the DLL extension. Note also the definition file, which was referenced above. The interesting trick to creating Fortran DLLs that use API calls is the libraries. The Microsoft Fortran manual states that one should use the 'C' compatible libraries for mixed language applications. That is not true here, so the LDLLFEW.LIB called is the regular version of LDLLFEW, not the 'C' version (LDLLFEWC.LIB). The NOQWIN.LIB library is used here since this will be a dynamic link library, and not a QuickWin application. If you do not already have NOQWIN.LIB in your library files, see the Advanced Topics Manual for details on how to generate it. SUMMARY ------- Fortran dynamic link libraries that utilize Windows API calls to send text strings to a Visual Basic application can be created using Microsoft's Fortran 5.1 compiler, and Visual Basic 3.0. This example application demonstrates a relatively simple way to send status or error messages as strings from a running Fortran DLL to a VB text box and list box to provide information on the status of the DLL as it executes. This methodology may be used with large DLLs written in languages other than Fortran. CAVEATS ------- There are no guarantees that this methodology for using API calls from a Fortran DLL to a Visual Basic program will work for all applications without errors. Likewise, there may be better ways to utilize the 700+ API calls to better refine the interface between the DLL and the VB program. Feel free to forward your suggestions, and the author will issue an updated version of these files, if warranted. Note that while the DLL is running, it does not 'yield' to Windows; therefore, there will be no response to keyboard or mouse events until either the second MessageBox is displayed by the DLL, or the DLL is exited. Evidently, the YIELDQQ subroutine (similar to VB's Do Events) is not available (or might not be applicable) for DLLs; the YIELD API call apparently does not do anything for a running DLL. Therefore, running a large DLL will still 'freeze' Windows until it is complete, but at least the user will know that the DLL is still active, based upon its status messages. REFERENCES ---------- Windows Programming for Fortran, J. Ribar MS Fortran 5.1 manuals (Advanced Topics) MS Visual Basic 3.0, Professional Edition, manuals Visual Basic Programmers Guide to the Windows API, D. Appleman Miscellaneous Fortran/Windows files, available from the MS Languages - Fortran forum on Compuserve (GO MSLANG)